package org.asg.dis.filter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.asg.dis.service.JwtService;
import org.asg.dis.service.RbacService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;


@Component
public class JwtRbacAuthentication extends OncePerRequestFilter {

    @Autowired
    private JwtService jwtService;
    @Autowired
    private RbacService rbacService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {
        String token = getTokenFromRequest(request);
        if (token != null && validateToken(token.strip())) {
            try {
                Claims claims = JwtService.parseToken(token.strip());
                System.out.println("取出的claims");
                System.out.println(claims);

                Long userId = claims.get("userId", Long.class);
                String roles = claims.get("roles", String.class);
                System.out.println("next: SecurityContextHolder.getContext().setAuthentication");

                SecurityContextHolder.getContext().setAuthentication(
                    new UsernamePasswordAuthenticationToken(
                        claims,
                        null,
                        mapAuthorities(roles)
                ));
                System.out.println("filter done");
            } catch (Exception e) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("Unauthorized: " + e.getMessage());
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
    private Collection<? extends GrantedAuthority> mapAuthorities(List<String> roles) {
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // 添加前缀
                .collect(Collectors.toList());
    }
    private Collection<? extends GrantedAuthority> mapAuthorities(String role) {
        return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)); // 添加前缀并返回单一角色集合
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String ret = header.substring(7);
            System.out.println("token 在裁剪后: " + ret);
            if(ret.startsWith("Bearer ")){
                ret = ret.substring(7);
                System.out.println("token 在2裁剪后: " + ret);
            }
            return ret;
        }
        return null;
    }
    private boolean validateToken(String token) {
        // 首先解析并验证 token 的合法性和过期性
        Claims claims;
        try {
            claims = JwtService.parseToken(token);
        } catch (ExpiredJwtException e) {
            // token 过期
            return false;
        } catch (Exception e) {
            // token 无效
            return false;
        }

        // 检查 token 是否存在于 Redis 中
        String redisTokenKey = "token:" + token;
        Boolean isTokenStored = redisTemplate.hasKey(redisTokenKey);
        return Boolean.TRUE.equals(isTokenStored);
    }

}

